#include "app.hpp"

Application::Application()
{
	screen = 0;
	frameskip = true;
	slow = 1;
}

Application::~Application()
{
}

#include "SDL.h"

enum
{
	SUCCESS,
	SDLFAIL,
	DISKFAIL,
	VIDFAIL,
	AUDFAIL,
	INFAIL,
	MUSFAIL
};

#include "input.hpp"
#include "video.hpp"
#include "audio.hpp"
#include "music.hpp"
#include "disk.hpp"

#include "title.hpp"
#include "select.hpp"
#include "game.hpp"

#include <sstream>

int Application::Run(int argct, char *args[])
{
	ScreenChange(&Title);

	Vec2i res (640, 480);
	Uint32 vflags = 0;
	std::stringstream arg;
	int stg;
	int ckpt;
	for (int i = 1; i < argct; ++i)
	{
		if (args[i][0] == '-')
		{
			switch (args[i][1])
			{
			case 's':
				if (i + 1 < argct)
				{
					stg = args[i+1][0] - '0';
					if (1 <= stg && stg <= 7)
					{
						ScreenChange(&Game);
						Game.StageSet(stg);
						ckpt = args[i+1][1] - '0';
						if (0 <= ckpt && ckpt <= 3)
							Game.CkptSet(ckpt);
					}
				}
				break;
			case 'q':
				ScreenChange(&Select);
				break;

			case 'f':
				vflags |= SDL_FULLSCREEN;
				break;

			case 'r':
				if (i + 1 < argct)
				{
					arg.str("");
					arg.clear();
					arg << args[i + 1];
					arg >> res.x;
				}
				if (arg.fail())
					break;

				if (i + 2 < argct)
				{
					arg.str("");
					arg.clear();
					arg << args[i + 2];
					arg >> res.y;
				}
				if (i + 2 >= argct || arg.fail())
				{
					res.x = 640;
					++i;
					break;
				}

				break;

			default:
				;
			}
		}
	}

	Uint32 sdlflags = SDL_INIT_EVERYTHING;
#ifndef WIN32
	sdlflags |= SDL_INIT_EVENTTHREAD;
#endif

	int error = SUCCESS;

	if (SDL_Init(sdlflags) == -1)
		error = SDLFAIL;
	if (!Dsk.Open(args[0]) || !Dsk.AutoSetup())
		error = DISKFAIL;
	if (!Vid.Open(res.x, res.y, vflags))
		error = VIDFAIL;
	if (!Aud.Open())
		error = AUDFAIL;
	if (!Inp.Open())
		error = INFAIL;

	if (!Mus.Open())
		error = MUSFAIL;

	if (error == SUCCESS)
	{
		Loop();
	}

	Vid.Close();
	Aud.Close();
	Inp.Close();
	Dsk.Close();
	Mus.Close();

	SDL_Quit();

	return SUCCESS;
}

void Application::Loop()
{
	Uint32 lut;
	Uint32 lft;

	while (screen)
	{
		Screen *oldscreen = screen;
		
		if (!screen->Open())
			Exit();

		lut = SDL_GetTicks();
		lft = SDL_GetTicks();
		//Uint32 screent0 = SDL_GetTicks();

		while (oldscreen == screen)
		{
			SDL_Event event;

			while (SDL_PollEvent(&event))
			{
				switch (event.type)
				{
				case SDL_QUIT:
					Exit();
					break;

				case SDL_VIDEORESIZE:
					Vid.WinResize(event.resize.w, 
							event.resize.h,
							Vid.KEEPASPECT);
					break;

				case SDL_ACTIVEEVENT:
					if (Game.Pause() || event.active.state 
							== SDL_APPMOUSEFOCUS)
						break;

					if (event.active.gain)
					{
						Mus.Resume();
						Aud.Resume();
					}
					else
					{
						Mus.Stop();
						Aud.Pause();
					}
					break;

				default:
					Inp.Event(event);
				}
			}

			Uint32 mspu = slow * 16;
			if (!(SDL_GetAppState() & SDL_APPINPUTFOCUS) ||
				!(SDL_GetAppState() & SDL_APPACTIVE))
			{
				lut = SDL_GetTicks();
			}
			else while (SDL_GetTicks() >= lut + mspu
			       && oldscreen == screen)
			{
				screen->In();
				if (screen == oldscreen)
					screen->Update();
				Inp.Update();

				if (frameskip)
					lut += mspu;
				else
					lut = SDL_GetTicks();
			}


			float upms = 1.f / mspu;
			float upfrac = (float)(SDL_GetTicks() - lut) * upms;
			Uint32 mspf = SDL_GetTicks() - lft;
			
			if (mspf > 0)
			{
				Vid.Clear();
				if (oldscreen == screen)
					//&& SDL_GetTicks() >= screent0 + mspu)
				{
					screen->Out(upfrac);
					lft = SDL_GetTicks();
				}
				Vid.DrawEnd();
			}
		}

		oldscreen->Close();
	}
}

Application App;
